﻿using System;
using System.Collections.Generic;
using IBNet.UtilsDataStructuresAndExtensions;

namespace IBNet.DayTrade.Indicators
{
   public class RateOfChange : Indicator
   {
      private readonly Func<FixedLengthCollection<PriceBar>, decimal> _rocIndicator;
      private readonly int _farIndex;
      private readonly int _rocLength;
      private readonly PriceBarType _barType;      

      public override decimal? CalculateIndicator(FixedLengthCollection<PriceBar> priceBars)
      {
         if (_rocLength >= priceBars.Count)
            return null;

         decimal? rocValue = _rocIndicator(priceBars);
         return rocValue;
      }

      public RateOfChange(int rocLength, PriceBarType barType)
         : this(rocLength, barType, "RateOfChange")
      {
      }

      public RateOfChange(int rocLength, PriceBarType barType, string name)
         : base(name)
      {
         if (String.IsNullOrEmpty(name))
            throw new ArgumentNullException("name");
         if (rocLength < 1)
            throw new ArgumentException("rocLength must be greater than or equal to 1.");

         _farIndex = rocLength;
         _rocLength = rocLength;
         _barType = barType;

         switch (barType)
         {
            case PriceBarType.Open:
            {
               _rocIndicator = delegate(FixedLengthCollection<PriceBar> priceBars)
               {
                  if (priceBars[0].Open == priceBars[_farIndex].Open)
                     return 0.0m;

                  decimal rocValue =
                     PercentDifference(priceBars[0].Open, priceBars[_farIndex].Open);
                  return rocValue;
               };
               break;
            }
            case PriceBarType.High:
            {
               _rocIndicator = delegate(FixedLengthCollection<PriceBar> priceBars)
               {
                  if (priceBars[0].High == priceBars[_farIndex].High)
                     return 0.0m;

                  decimal rocValue =
                     PercentDifference(priceBars[0].High, priceBars[_farIndex].High);
                  return rocValue;
               };
               break;
            }
            case PriceBarType.Low:
            {
               _rocIndicator = delegate(FixedLengthCollection<PriceBar> priceBars)
               {
                  if (priceBars[0].Low == priceBars[_farIndex].Low)
                     return 0.0m;

                  decimal rocValue =
                     PercentDifference(priceBars[0].Low, priceBars[_farIndex].Low);
                  return rocValue;
               };
               break;
            }
            case PriceBarType.Close:
            {
               _rocIndicator = delegate(FixedLengthCollection<PriceBar> priceBars)
               {
                  if (priceBars[0].Close == priceBars[_farIndex].Close)
                     return 0.0m;

                  decimal rocValue =
                     PercentDifference(priceBars[0].Close, priceBars[_farIndex].Close);
                  return rocValue;
               };
               break;
            }
            case PriceBarType.Volume:
            {
               _rocIndicator = delegate(FixedLengthCollection<PriceBar> priceBars)
               {
                  if (priceBars[0].Volume == priceBars[_farIndex].Volume)
                     return 0.0m;

                  decimal rocValue =
                     PercentDifference(priceBars[0].Volume, priceBars[_farIndex].Volume);
                  return rocValue;
               };
               break;
            }
         }
      }

      private decimal PercentDifference(decimal quantity1, decimal quantity2)
      {
         decimal percentDifference =
            100 * (quantity1 - quantity2) / quantity2;
         return percentDifference;
      }
   }
}
